
#include "WkFFmpeg.h"

WkFFmpeg::WkFFmpeg(WkJavaCall* wkJavaCall1 ,PlayerStatus* playerStatus , const char* source)
{
   this->wkJavaCall = wkJavaCall1 ;
    this->playerStatus = playerStatus ;
    this->source= source ;
    exit = false;
    pthread_mutex_init(&init_mutex, NULL);
    pthread_mutex_init(&seek_mutex, NULL);
}

WkFFmpeg::~WkFFmpeg()
{
    pthread_mutex_destroy(&init_mutex);
    pthread_mutex_destroy(&seek_mutex);
}


int interruptCallBack( void *ctx)
{
    WkFFmpeg * fFmpeg = (WkFFmpeg *) ctx ;
    if(fFmpeg->playerStatus->exit)
    {
        return  AVERROR_EOF ;
    }
    // 0 : 继续等待 直到正常
    // 1、 结束等待
    return 0 ;
}


void*  decodeFFmpeg( void * data)
{
    WkFFmpeg* wkFFmpeg =  (WkFFmpeg*) data ;
    wkFFmpeg->decodecFFmpegThread();
    pthread_exit(&wkFFmpeg->ffmpegThread) ;
}

void WkFFmpeg::preparePlayer()
{
    pthread_create(&ffmpegThread ,NULL , decodeFFmpeg ,this) ;
}

void WkFFmpeg::decodecFFmpegThread()
{
    LOGI("decodecFFmpegThread..."  );
    pthread_mutex_lock(&init_mutex) ;
    av_register_all() ;//注册设备
    avformat_network_init() ;//初始化网络
    avFormatContext = avformat_alloc_context() ;
    // 防止因网络等问题导致长时间等待. 要强行中断
    LOGI("interruptCallBack.."  );
    avFormatContext->interrupt_callback.callback = interruptCallBack;
    avFormatContext->interrupt_callback.opaque = this ;

    LOGI("尝试打开url"  );
    if( avformat_open_input(&avFormatContext ,source ,NULL ,NULL) !=0)
    {
        LOGE("打开url失败 source= %s" ,source);
        wkJavaCall->onCallError(CHILD_THREAD , 1001 ,"打开url失败") ;
        exit = true ;
        pthread_mutex_unlock(&init_mutex) ;
        return;
    }

    LOGI("打开url ok  source= %s" ,source);

    if(avformat_find_stream_info(avFormatContext,NULL) < 0)
    {
        LOGE("查找流信息失败");
        wkJavaCall->onCallError(CHILD_THREAD ,1002 ,"查找流信息失败") ;
        exit= true ;
        pthread_mutex_unlock(&init_mutex) ;
        return;
    }

    for(int i=0 ; i<avFormatContext->nb_streams;i++)
    {
        if(avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            LOGI("找到音频信息 ")
            if( wkAudio == NULL)
            {
                LOGI(" 初始化....... .sample_rate= %d" ,avFormatContext->streams[i]->codecpar->sample_rate );
                wkAudio = new WkAudio(playerStatus, avFormatContext->streams[i]->codecpar->sample_rate, wkJavaCall);
                wkAudio->streamIndex = i;
                wkAudio->avCodecParameters = avFormatContext->streams[i]->codecpar;
                wkAudio->duration =  int(avFormatContext->duration / AV_TIME_BASE);
                wkAudio->time_base = avFormatContext->streams[i]->time_base;
                duration = wkAudio->duration;
            }
        }
    }// end for

    AVCodec * avCodec = avcodec_find_decoder(wkAudio->avCodecParameters->codec_id) ;
    if(!avCodec)
    {
        LOGE("没有找到合适的解码器");
        wkJavaCall->onCallError(CHILD_THREAD ,1003 ,"没有找到合适的解码器") ;
        exit= true ;
        pthread_mutex_unlock(&init_mutex) ;
        return;
    }


    wkAudio->avCodecContext = avcodec_alloc_context3(avCodec) ;
    if(!wkAudio->avCodecContext)
    {
        LOGE("给解码器分配内存失败") ;
        wkJavaCall->onCallError(CHILD_THREAD ,1004 ,"给解码器分配内存失败") ;
        exit= true ;
        pthread_mutex_unlock(&init_mutex) ;
        return;
    }

    if(avcodec_parameters_to_context(wkAudio->avCodecContext , wkAudio->avCodecParameters ) < 0 )
    {
        LOGE("ffmpeg AVCodecContext 数据拷贝失败") ;
        wkJavaCall->onCallError(CHILD_THREAD ,1005 ,"ffmpeg AVCodecContext 数据拷贝失败") ;
        exit= true ;
        pthread_mutex_unlock(&init_mutex) ;
        return;
    }

    if(avcodec_open2(wkAudio->avCodecContext ,avCodec , 0) !=0)
    {
        LOGE("打开流失败");
        wkJavaCall->onCallError(CHILD_THREAD ,1006 ,"打开流失败") ;
        exit= true ;
        pthread_mutex_unlock(&init_mutex) ;
        return;
    }

    if(wkJavaCall != NULL)
    {
        if(playerStatus != NULL && !playerStatus->exit)
        {
            wkJavaCall->onPrepared(CHILD_THREAD);
        } else{
            exit = true;
        }
    }

    pthread_mutex_unlock(&init_mutex) ;

}

void WkFFmpeg::start() {
    LOGI("ffmpeg 开始 解码...")
    if(wkAudio ==NULL)
    {
        return;
    }

    wkAudio->play() ;
    while (playerStatus != NULL  &&  !playerStatus->exit)
    {
        if(playerStatus->seek)
        {
            av_usleep(1000 * 100);
            continue;
        }

        if(wkAudio->queuePacket->getQueueSize() > 100)
        {
            av_usleep( 1000 * 100) ;// 100毫秒
            continue ;
        }

        AVPacket *avPacket = av_packet_alloc() ;
        if(av_read_frame(avFormatContext ,avPacket) == 0 )
        {
//            LOGI("111 播放情况= %lf, %d" ,wkAudio->clock , wkAudio->duration) ;
            if(avPacket->stream_index == wkAudio->streamIndex)
            {
                wkAudio->queuePacket->putAvPacket(avPacket) ;
            }else{
                // 不是音频帧
                av_packet_free(&avPacket) ;
                av_free(avPacket) ;
            }
        }else {
            // 读取帧数据失败 或读取完成
            // 没法单独分开
            av_packet_free(&avPacket) ;
            av_free(avPacket) ;

            while(playerStatus !=NULL && !playerStatus->exit)
            {
                if(wkAudio->queuePacket->getQueueSize() > 0)
                {
                    av_usleep(1000 *100) ;
                    continue ;
                }else {
                    playerStatus->exit = true ;
                    break ;
                }
            }
            break ;
        }
    }//end while


    // 播放完成 ，退出循环
    if(wkJavaCall != NULL)
    {
        wkJavaCall->onCallComplate(CHILD_THREAD);
    }
    exit = true;


}

void WkFFmpeg::pause()
{
    if(wkAudio != NULL)
    {
        wkAudio->pause();
    }
}

void WkFFmpeg::resume()
{
    if(wkAudio != NULL)
    {
        wkAudio->resume();
    }
}

void WkFFmpeg::release()
{
    LOGI("开始释放Ffmpe");
    playerStatus->exit = true;
    pthread_mutex_lock(&init_mutex);
    int sleepCount = 0;
    while (!exit)
    {
        // 强制退出
        if(sleepCount > 1000)
        {
            exit = true;
        }
        LOGI("wait ffmpeg  exit %d", sleepCount);
        sleepCount++;
        //暂停10毫秒
        av_usleep(1000 * 10);
    }

    LOGI("释放 Audio");
    if(wkAudio != NULL)
    {
        wkAudio->release();
        delete(wkAudio);
        wkAudio = NULL;
    }

    LOGI("释放 封装格式上下文");

    if(avFormatContext != NULL)
    {
        avformat_close_input(&avFormatContext);
        avformat_free_context(avFormatContext);
        avFormatContext = NULL;
    }
    LOGE("释放 callJava");

    if(wkJavaCall != NULL)
    {
        wkJavaCall = NULL;
    }
     LOGE("释放 playstatus");

    if(playerStatus != NULL)
    {
        playerStatus = NULL;
    }
    pthread_mutex_unlock(&init_mutex);
}

void WkFFmpeg::seek(int64_t secds)
{
    if(duration <= 0)
    {
        return;
    }
    if(secds >= 0 && secds <= duration)
    {
        if(wkAudio != NULL)
        {
            playerStatus->seek = true;
            wkAudio->queuePacket->clearAvPacket();
            wkAudio->clock = 0;
            wkAudio->last_tiem = 0;
            pthread_mutex_lock(&seek_mutex);
            int64_t rel = secds * AV_TIME_BASE;
            avformat_seek_file(avFormatContext, -1, INT64_MIN, rel, INT64_MAX, 0);
            pthread_mutex_unlock(&seek_mutex);
            playerStatus->seek = false;
        }
    }
}

void WkFFmpeg::setVolume(int percent)
{
    if(wkAudio != NULL)
    {
        wkAudio->setVolume(percent);
    }
}

void WkFFmpeg::setMute(int mute)
{
    if(wkAudio != NULL)
    {
        wkAudio->setMute(mute);
    }
}

void WkFFmpeg::setPitch(float pitch)
{
    if(wkAudio != NULL)
    {
        wkAudio->setPitch(pitch);
    }
}

void WkFFmpeg::setSpeed(float speed)
{
    if(wkAudio != NULL)
    {
        wkAudio->setSpeed(speed);
    }
}

int WkFFmpeg::getSampleRate()
{
    if(wkAudio != NULL)
    {
        return wkAudio->avCodecContext->sample_rate;
    }
    return 0;
}

void WkFFmpeg::startStopRecord(bool start)
{
    if(wkAudio != NULL)
    {
        wkAudio->startStopRecord(start);
    }
}

void WkFFmpeg::setCircle(bool isCircle)
{
    this->isCircle= isCircle  ;
}

